﻿using System;
using NSubstitute.Core;

// Disable nullability for client API, so it does not affect clients.
#nullable disable annotations

namespace NSubstitute.ExceptionExtensions
{
    public static class ExceptionExtensions
    {
        /// <summary>
        /// Throw an exception for this call.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="ex">Exception to throw</param>
        /// <returns></returns>
        public static ConfiguredCall Throws(this object value, Exception ex) =>
            value.Returns(_ => throw ex);

        /// <summary>
        /// Throw an exception of the given type for this call.
        /// </summary>
        /// <typeparam name="TException">Type of exception to throw</typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static ConfiguredCall Throws<TException>(this object value)
            where TException : notnull, Exception, new()
        {
            return value.Returns(_ => throw new TException());
        }

        /// <summary>
        /// Throw an exception for this call, as generated by the specified function.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="createException">Func creating exception object</param>
        /// <returns></returns>
        public static ConfiguredCall Throws(this object value, Func<CallInfo, Exception> createException) =>
            value.Returns(ci => throw createException(ci));

        /// <summary>
        /// Throw an exception for this call made with any arguments.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="ex">Exception to throw</param>
        /// <returns></returns>
        public static ConfiguredCall ThrowsForAnyArgs(this object value, Exception ex) =>
            value.ReturnsForAnyArgs(_ => throw ex);

        /// <summary>
        /// Throws an exception of the given type for this call made with any arguments.
        /// </summary>
        /// <typeparam name="TException">Type of exception to throw</typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static ConfiguredCall ThrowsForAnyArgs<TException>(this object value)
            where TException : notnull, Exception, new()
        {
            return value.ReturnsForAnyArgs(_ => throw new TException());
        }

        /// <summary>
        /// Throws an exception for this call made with any arguments, as generated by the specified function.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="createException">Func creating exception object</param>
        /// <returns></returns>
        public static ConfiguredCall ThrowsForAnyArgs(this object value, Func<CallInfo, Exception> createException) =>
            value.ReturnsForAnyArgs(ci => throw createException(ci));
    }
}
